package longbottom.XLkprob;

import java.text.DecimalFormat;

import org.achartengine.model.CategorySeries;
import org.apache.commons.math3.distribution.BinomialDistribution;
import org.apache.commons.math3.exception.OutOfRangeException;

import android.content.Context;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

/**
 * @author pumaman 
 * 		   A "situation" is a case that we want to evaluation the
 *         probability on. It will calculate a probability of each shot causing
 *         an unsaved wound given the dice rolls required. It then uses the
 *         binomial distribution to calculate the probabilities.
 */
public class SimpleSituation implements CalculatesDistribution {

	private double pKill; // probability of causing an unsaved wound
	private int numberShots;
	private DecimalFormat twoDForm;
	public static final int REROLL1 = 2;
	public static final int REROLLFAIL = 3;
	
	
	/**
	 * @param die
	 *            , a number required to be rolled on a six sided die
	 * @return false is the value of die is out with the bounds. 7 represents an
	 *         automatic fail so is allowed (can't roll a 7 on a d6)!
	 */
	private boolean checkDiceBounds(int die) {
		if (die < 1 || die > 7)
			return false;
		else
			return true;
	}

	private static double calculateDieSuccess(int rollRequired, int reRollType) {

		//trivial cases
		if (rollRequired == 1)
			return 1.0;
		else if (rollRequired == 7) return 0.0;
		
		double a = (7 - rollRequired) / 6.0;

		if (reRollType == REROLLFAIL)
			return a + a*(1-a);
		else if (reRollType == REROLL1) {
			return a + a / 6.0;
		} else
			return a;

	}

	/**
	 * @param numberOfShots
	 *            number of shots; attempts to cause unsaved wounds
	 * @param requiredtoHit
	 *            minimum die score required to cause a hit
	 * @param requiredToWound
	 *            minimum die score required to cause a wound
	 * @param RequiredToSave
	 *            minimum die score to save the wound
	 * @throws OutOfRangeException
	 */
	public SimpleSituation(int numberOfShots, int requiredtoHit,
			int requiredToWound, int requiredToSave, int reRollHit,
			int reRollWound, int reRollSave) throws OutOfRangeException {

		// Check bounds
		if (numberOfShots < 1)
			throw new OutOfRangeException(numberOfShots, 1, null);
		if (!checkDiceBounds(requiredtoHit))
			throw new OutOfRangeException(requiredtoHit, 1, 7);
		if (!checkDiceBounds(requiredToWound))
			throw new OutOfRangeException(requiredtoHit, 1, 7);
		if (!checkDiceBounds(requiredToSave))
			throw new OutOfRangeException(requiredtoHit, 1, 7);

		pKill = calculateDieSuccess(requiredtoHit, reRollHit)
				* calculateDieSuccess(requiredToWound, reRollWound)
				* (1 - calculateDieSuccess(requiredToSave, reRollSave));
		numberShots = numberOfShots;

		twoDForm = new DecimalFormat("#.#");
	}

	@Override
	public int getMaxCasualties() {
		return numberShots;
	}

	public BinomialDistribution getDistribution() {
		BinomialDistribution a = new BinomialDistribution(numberShots, pKill);
		return a;
	}

	@Override
	public CategorySeries getDistributionSeries(Context context) {

		BinomialDistribution a = getDistribution();
		CategorySeries series = new CategorySeries((String) context
				.getResources().getText(R.string.distribution));

		for (int i = 0; i <= getMaxCasualties(); i++) {
			series.add(i + " casualty",
					Double.valueOf(twoDForm.format(a.probability(i) * 100)));
		}
		return series;
	}

	@Override
	public CategorySeries getCumulativeDistributionSeries(Context context) {

		BinomialDistribution a = getDistribution();
		CategorySeries series = new CategorySeries((String) context
				.getResources().getText(R.string.cumulative));

		series.add(0 + " casualty", 100.00);
		for (int i = 1; i <= getMaxCasualties(); i++) {
			series.add(i + " casualty", Double.valueOf(twoDForm.format((1 - a
					.cumulativeProbability(i - 1)) * 100)));

		}

		return series;
	}
	
	private static int getSpinnerValue(Spinner spinner) {
		int item = spinner.getSelectedItemPosition() + 1;
		return item;
	}
	
	public static SimpleSituation  parseControls(ViewGroup container){
		Context context = container.getContext();
		
		// find the UI elements
		EditText textShots = (EditText) container.findViewById(R.id.editText1);
		Spinner spinnerToHit = (Spinner) container.findViewById(R.id.spinner1);
		Spinner spinnerToWound = (Spinner) container
				.findViewById(R.id.spinner2);
		Spinner spinnerToSave = (Spinner) container.findViewById(R.id.spinner3);
		Spinner spinnerRRHit = (Spinner) container.findViewById(R.id.spinner4);
		Spinner spinnerRRWound = (Spinner) container
				.findViewById(R.id.spinner5);
		Spinner spinnerRRSave = (Spinner) container.findViewById(R.id.spinner6);

		// parse the data from the UI elements
		int nShots;
		try {
			nShots = Integer.parseInt(textShots.getText().toString());
		} catch (NumberFormatException e) {
			Toast toast = Toast.makeText(context, "Please enter a valid number",
					Toast.LENGTH_SHORT);
			toast.show();
			return null;
		}
		int toHit = getSpinnerValue(spinnerToHit);
		int toWound = getSpinnerValue(spinnerToWound);
		int toSave = getSpinnerValue(spinnerToSave);
		int rrHit = getSpinnerValue(spinnerRRHit);
		int rrWound = getSpinnerValue(spinnerRRWound);
		int rrSave = getSpinnerValue(spinnerRRSave);

		// use the data to set up the situation we wish to model
		SimpleSituation s;
		try {
			s = new SimpleSituation(nShots, toHit, toWound, toSave, rrHit,
					rrWound, rrSave);
		} catch (OutOfRangeException e) {
			Toast toast = Toast.makeText(context, "Please enter a valid number",
					Toast.LENGTH_SHORT);
			toast.show();
			return null;
		}	
		
		return s;
	}

}
